#include<vector>
#include<ctime>
#include<cmath>
#include<string>
#include<fstream>
#include"Util.h"
#include"StringPool.h"
#include "variable.h"
#include "OnlineTrainer3OGSibling.h"

OnlineTrainer3OGSibling::OnlineTrainer3OGSibling(int maxLength, DependencyPipe *pipe, ParserOptions *options, DependencyDecoder *decoder, Parameters *params) : OnlineTrainer2O(maxLength, pipe, options, decoder, params){
	probs_gtrips = vector<vector<vector<vector<double> > > >(maxLength);
	for(int i = 0; i < maxLength; i++){
		probs_gtrips[i] = vector<vector<vector<double> > >(maxLength);
		for(int j = 0; j < maxLength; j++){
			int nn1;
			if(j == i){
				nn1 = 0;
			}
			else if(j < i){
				nn1 = i;
			}
			else{
				nn1 = maxLength - i - 1;
			}
			probs_gtrips[i][j] = vector<vector<double> >(nn1);
			for(int k = 0; k < nn1; k++){
				int nn2;
				if(j < i){
					if(k < j){
						nn2 = k;
					}
					else if(k == j){
						nn2 = i;
					}
					else{
						nn2 = i - k - 1;
					}
					probs_gtrips[i][j][k] = vector<double>(nn2);
				}
				else{
					int kk = k + i + 1;
					if(kk < j){
						nn2 = k;
					}
					else if(kk == j){
						nn2 = maxLength - i - 1;
					}
					else{
						nn2 = maxLength - kk - 1;
					}
					probs_gtrips[i][j][k] = vector<double>(nn2);
				}
			}
		}
	}

	probs_gsibs = vector<vector<vector<vector<double> > > >(maxLength);
	probs_coords = vector<vector<vector<double> > >(maxLength);
	for(int i = 0; i < maxLength; i++){
		probs_gsibs[i] = vector<vector<vector<double> > >(maxLength);
		probs_coords[i] = vector<vector<double> >(maxLength);
		for(int j = 0; j < maxLength; j++){
			int nn;
			if(i == j){
				nn = 0;
			}
			else if(j < i){
				nn = i;
			}
			else{
				nn = maxLength;
			}
			probs_gsibs[i][j] = vector<vector<double> >(nn);
			probs_coords[i][j] = vector<double>(nn);
			for(int k = 0; k < nn; k++){
				if(i < j){
					if(k > i){
						probs_gsibs[i][j][k] = vector<double>(2);
					}
					else{
						probs_gsibs[i][j][k] = vector<double>(0);
					}
				}
				else{
					probs_gsibs[i][j][k] = vector<double>(2);
				}
			}
		}
	}
}

OnlineTrainer3OGSibling::~OnlineTrainer3OGSibling(){}

void OnlineTrainer3OGSibling::developOnline(std::vector<int> *instanceLengths, std::string *train_forest, int iter, Parameters *params_bk){
	ObjectReader in(train_forest->c_str());
	
	int numInstances = (int)(instanceLengths->size());
	
	int bb = 0;
	int per;
	for(int i = 0; i < numInstances; i++){
		int length = (*instanceLengths)[i];
		if(i % 400 == 0){
			per = i * 100 / numInstances;
			for(int mm = 0; mm < bb; mm++){
				printf("\b");
			}
			printf("%d%%", per);
			fflush(stdout);
			if(per >= 100){
				bb = 4;
			}
			else if(per >= 10){
				bb = 3;
			}
			else{
				bb = 2;
			}
		}
		
		DependencyInstance* inst = NULL;

		inst = ((DependencyPipe3OGSibling*)pipe)->readInstance(in, length, probs, probs_trips, probs_sibs, probs_grands, probs_gchs, probs_gtrips, probs_gsibs, probs_coords, nt_probs, params);

		double upd = (double) (options->numIters * numInstances - (numInstances * (iter - 1) + (i + 1)) + 1);

		int K = options->trainK;

		vector<pair<FeatureVector*, string*> >* d = NULL;

		d = ((DependencyDecoder3OGSibling*)decoder)->decodeProjective(inst, probs, probs_trips, probs_sibs, probs_grands, probs_gchs, probs_gtrips, probs_gsibs, probs_coords, nt_probs, K);

		if(options->lm == 0){
			params->updateParametersMIRA(inst, *d, upd);
		}
		else{
			params->updateParemetersAP(inst, *d, upd);
		}

		for(int j = 0; j < params_bk->size; j++){
			params_bk->total[j] += params->parameters[j];
		}
		
		delete(inst);
		
		if(d != NULL){
			int len = (int)(d->size());
			for(int j = 0; j < len; j++){
				pair<FeatureVector*, string*> p = (*d)[j];
				if(p.first != NULL){
					delete(p.first);
				}
				if(p.second != NULL){
					pool->push_back(p.second);
				}
			}
			delete(d);
		}	
	}
	
	for(int mm = 0; mm < bb; mm++){
		printf("\b");
	}
	printf("%d", numInstances);
	fflush(stdout);
}

void OnlineTrainer3OGSibling::trainOnline(std::vector<int> *instanceLengths, std::string *train_forest, int iter){
	ObjectReader in(train_forest->c_str());

	int numInstances = (int)(instanceLengths->size());

	
	for(int i = 0; i < numInstances; i++){

		int length = (*instanceLengths)[i];
		
		DependencyInstance* inst = NULL;

		inst = ((DependencyPipe3OGSibling*)pipe)->readInstance(in, length, probs, probs_trips, probs_sibs, probs_grands, probs_gchs, probs_gtrips, probs_gsibs, probs_coords, nt_probs, params);

		double upd = (double) (options->numIters * numInstances - (numInstances * (iter - 1) + (i + 1)) + 1);

		int K = options->trainK;

		vector<pair<FeatureVector*, string*> >* d = NULL;

		d = ((DependencyDecoder3OGSibling*)decoder)->decodeProjective(inst, probs, probs_trips, probs_sibs, probs_grands, probs_gchs, probs_gtrips, probs_gsibs, probs_coords, nt_probs, K);

		if(options->lm == 0){
			params->updateParametersMIRA(inst, *d, upd);
		}
		else{
			params->updateParemetersAP(inst, *d, upd);
		}
		delete(inst);
		
		if(d != NULL){
			int len = (int)(d->size());
			for(int i = 0; i < len; i++){
				pair<FeatureVector*, string*> p = (*d)[i];
				if(p.first != NULL){
					delete(p.first);
				}
				if(p.second != NULL){
					pool->push_back(p.second);
				}
			}
			delete(d);
		}
	}
	
	printf("%d", numInstances);
}

void OnlineTrainer3OGSibling::developWriteLog(Parameters *params_bk, std::string *devfile){
	DependencyReader *devReader = DependencyReader::createDependencyReader(*(options->format), options->discourseMode, pipe->labeled, options->hasEnd);
	DependencyWriter *tempWriter = DependencyWriter::createDependencyWriter(*(options->format), pipe->labeled, options->hasEnd);
	tempWriter->startWriting("tmp/result.tmp");
	devReader->startReading(devfile->c_str());

	DependencyInstance* instance = devReader->getNext();
	while(instance != NULL){
		vector<string*>* forms = instance->forms;
		int length = (int)(forms->size());

		((DependencyPipe3OGSibling*)pipe)->fillFeatureVectors(instance, probs, probs_trips, probs_sibs, probs_grands, probs_gchs, probs_gtrips, probs_gsibs, probs_coords, nt_probs, params_bk);

		int K = options->testK;
		vector<pair<FeatureVector*, string*> >* d = NULL;

		d = ((DependencyDecoder3OGSibling*)decoder)->decodeProjective(instance, probs, probs_trips, probs_sibs, probs_grands, probs_gchs, probs_gtrips, probs_gsibs, probs_coords, nt_probs, K);

		vector<string*>* res = Util::split((*d)[0].second, ' ');
		vector<string*>* pos = instance->postags;
		vector<string*>* lemma = instance->lemmas;
		vector<string*>* cpos = instance->cpostags;
		vector<string*> formsNoRoot(length - 1);
		vector<string*> lemmaNoRoot(length - 1);
		vector<string*> posNoRoot(length - 1);
		vector<string*> cposNoRoot(length - 1);
		vector<string*> labels(length - 1);
		vector<int> heads(length - 1);
		
		for(int j = 0; j < length - 1; j++){
			formsNoRoot[j] = (*forms)[j + 1];
			lemmaNoRoot[j] = (*lemma)[j + 1];
			cposNoRoot[j] = (*cpos)[j + 1];
			posNoRoot[j] = (*pos)[j + 1];
			int position = (int)((*res)[j]->find('|'));
			string head = (*res)[j]->substr(0, position);
			position = (int)((*res)[j]->find(':'));
			string lab = (*res)[j]->substr(position + 1, (*res)[j]->length() - position -1);
			heads[j] = Util::stringToInt(&head);
			labels[j] = pipe->types[Util::stringToInt(&lab)];
			//cout<<(*res)[j]->c_str()<<'\t'<<formsNoRoot[j]->c_str()<<'\t'<<posNoRoot[j]->c_str()<<'\t'<<head.c_str()<<'\t'<<labels[j]->c_str()<<endl;
		}
		tempWriter->write(formsNoRoot,lemmaNoRoot, cposNoRoot, posNoRoot, labels, heads);
		int l = (int)(res->size());
		for(int j = 0; j < l; j++){
			pool->push_back((*res)[j]);
		}
		delete(res);
		delete(instance);
		if(d != NULL){
			int len = (int)(d->size());
			for(int i = 0; i < len; i++){
				pair<FeatureVector*, string*> p = (*d)[i];
				if(p.first != NULL){
					delete(p.first);
				}
				if(p.second != NULL){
					pool->push_back(p.second);
				}
			}
			delete(d);
		}
		instance = devReader->getNext();
	}
	tempWriter->finishWriting();
	devReader->finishReading();
	delete(devReader);
	delete(tempWriter);
}

void OnlineTrainer3OGSibling::outputParses(DependencyInstance *instance){
	((DependencyPipe3OGSibling*)pipe)->fillFeatureVectors(instance, probs, probs_trips, probs_sibs, probs_grands, probs_gchs, probs_gtrips, probs_gsibs, probs_coords, nt_probs, params);

	int K = options->testK;

	int length = instance->length();

	vector<pair<FeatureVector*, string*> >* d = NULL;

	d = ((DependencyDecoder3OGSibling*)decoder)->decodeProjective(instance, probs, probs_trips, probs_sibs, probs_grands, probs_gchs, probs_gtrips, probs_gsibs, probs_coords, nt_probs, K);

	vector<string*>* res = Util::split((*d)[0].second, ' ');
	vector<string*>* forms = instance->forms;
	vector<string*>* pos = instance->postags;
	vector<string*>* lemma = instance->lemmas;
	vector<string*>* cpos = instance->cpostags;
	vector<string*> formsNoRoot(length - 1);
	vector<string*> lemmaNoRoot(length - 1);
	vector<string*> cposNoRoot(length - 1);
	vector<string*> posNoRoot(length - 1);
	vector<string*> labels(length - 1);
	vector<int> heads(length - 1);
		
	for(int j = 0; j < length - 1; j++){
		formsNoRoot[j] = (*forms)[j + 1];
		lemmaNoRoot[j] = (*lemma)[j + 1];
		cposNoRoot[j] = (*cpos)[j + 1];
		posNoRoot[j] = (*pos)[j + 1];
		int position = (int)((*res)[j]->find('|'));
		string head = (*res)[j]->substr(0, position);
		position = (int)((*res)[j]->find(':'));
		string lab = (*res)[j]->substr(position + 1, (*res)[j]->length() - position -1);
		heads[j] = Util::stringToInt(&head);
		labels[j] = pipe->types[Util::stringToInt(&lab)];
		//cout<<(*res)[j]->c_str()<<'\t'<<formsNoRoot[j]->c_str()<<'\t'<<posNoRoot[j]->c_str()<<'\t'<<head.c_str()<<'\t'<<labels[j]->c_str()<<endl;
	}

	pipe->outputInstance(formsNoRoot, lemmaNoRoot, cposNoRoot, posNoRoot, labels, heads);
	int l = (int)(res->size());
	for(int j = 0; j < l; j++){
		pool->push_back((*res)[j]);
	}
	delete(res);
	if(d != NULL){
		int len = (int)(d->size());
		for(int i = 0; i < len; i++){
			pair<FeatureVector*, string*> p = (*d)[i];
			if(p.first != NULL){
				delete(p.first);
			}
			if(p.second != NULL){
				pool->push_back(p.second);
			}
		}
		delete(d);
	}
}